/**
 * 
 */
package fb.chatter.controller;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import fb.chatter.constant.Constant;
import fb.chatter.model.RequestedFriendsModel;
import fb.chatter.model.User;
import fb.chatter.model.Users;
import fb.chatter.utils.ExceptionUtils;
import fb.chatter.utils.HttpConnectionUtils;
import fb.chatter.utils.JSONUtils;
import fb.chatter.utils.VietComparator;
import fb.chatter.utils.XMLUtils;

/**
 * @author hoannguyen
 * 
 */
public class FriendListFormLoader extends Task<Users> {

	/**
	 * The separator of declaration and the body in the FriendListFormTemplate.fxml file
	 */
	private static final String ANCHOR_PANE = "<AnchorPane";

	/**
	 * The path of the FXML file that contains the modified view
	 */
	private static final String FRIEND_LIST_FILE_PATH = "src/fb/chatter/view/FriendListForm.fxml";
	
	/**
	 * The FriendListFormTemplate.xml file path
	 */
	private static final String FRIEND_LIST_TEMPLATE_FILE_PATH = "../view/FriendListFormTemplate.fxml";
	
	/**
	 * This method will called when the FriendListFormLoader is started.
	 */
	@Override
	protected Users call() throws Exception {
		
		Users friends = getFriendListWithoutGettingPhotos(150);
		final VietComparator vietComparator = new VietComparator();
		Collections.sort(friends.getData(), new Comparator<User>() {

			@Override
			public int compare(User u1, User u2) {
				return vietComparator.compare(u1.getName(), u2.getName());
			}
		});
		RequestedFriendsModel.getInstance().setFriends(friends);
		return null;
	}
	
	/**
	 * This method is called when the call() method is finished.
	 */
	@Override
	protected void succeeded() {
		
		loadViewAndShowForm();
	}
	
	/**
	 * To get friend list with the given limit number
	 * token.
	 * 
	 * @param limit if NULL, get all friends
	 * @return
	 */
	private Users getFriendListWithoutGettingPhotos(Integer limit) {

		// Get list of Facebook friends
		String friendsURL = "https://graph.facebook.com/me/friends?fields=username,name&access_token=" + Constant.ACCESS_TOKEN;
		if(limit != null){
			friendsURL += "&limit=" + String.valueOf(limit.intValue());
		}
		String friendsJSON = HttpConnectionUtils.doGetTextRequest(friendsURL);
		Users friends = (Users) JSONUtils.conveJSONToObject(friendsJSON, Users.class);
		System.out.println(friendsJSON);

		return friends;
	}

	/**
	 * To load controls and show the FriendListForm with the this.friends
	 */
	private void loadViewAndShowForm(){

		try {
			// Build controls
			buildControls(RequestedFriendsModel.getInstance().getFriends());
			
			// Load and show controls
			File f = new File(FRIEND_LIST_FILE_PATH);
			FXMLLoader fxmlLoader = new FXMLLoader();
			fxmlLoader.setLocation(f.toURI().toURL());
			AnchorPane friendListRootPane = (AnchorPane) fxmlLoader.load();
			
			// Update controls (friends' names)
			updateControls(friendListRootPane, RequestedFriendsModel.getInstance().getFriends());
			
			// Download and update friends' pictures
			FriendListPictureLoader friendListPictureLoader =  new FriendListPictureLoader();
			friendListPictureLoader.setFriendListRootPane(friendListRootPane);
			Thread photoLoaderThread = new Thread(friendListPictureLoader);
			photoLoaderThread.start();
			
		} catch (MalformedURLException e) {
			ExceptionUtils.log(e);
		} catch (IOException e) {
			ExceptionUtils.log(e);
		}
	}
	
	/**
	 * To build controls on FriendListForm UI.
	 * 
	 * @param friends 
	 */
	private void buildControls(Users friends) {

		// Get content of the FriendListForm.xml file
		InputStream is = getClass().getResourceAsStream(FRIEND_LIST_TEMPLATE_FILE_PATH);
		StringWriter writer = new StringWriter();
		String encoding = "UTF-8";
		try {
			IOUtils.copy(is, writer, encoding);
			is.close();
		} catch (IOException e) {
			ExceptionUtils.log(e);
		}
		String content = writer.toString();

		// Get header of content
		String header = content.substring(0, content.indexOf(ANCHOR_PANE) - 1);

		// Get XML body of content
		String xmlBody = content.substring(content.indexOf(ANCHOR_PANE));

		// Create XML document from the given XML body
		Document doc = XMLUtils.buildXMLDocument(xmlBody);

		// Get the friend panel element (that panel contains 01 textArea and 01
		// imageView)
		String xpathExpression = "/AnchorPane/children/ScrollPane/content/AnchorPane/children/FlowPane/children/AnchorPane";
		Element friendPaneElement = XMLUtils.getElement(doc, xpathExpression);

		// Loop for more cloned friendPaneElement
		for (int i = 0; i < friends.getData().size(); i++) {

			User f = friends.getData().get(i);
			
			// Create clonedFriendPaneElement of friendPaneElement
			Element clonedFriendPaneElement = (Element) friendPaneElement.cloneNode(true);

			// Modify textArea
			List<Node> textAreaList1 = XMLUtils.convert(clonedFriendPaneElement.getElementsByTagName("TextArea"));
			((Element) textAreaList1.get(0)).setAttribute("id", "txa" + f.getId());

			// Modify imageView
			List<Node> imvList1 = XMLUtils.convert(clonedFriendPaneElement.getElementsByTagName("ImageView"));
			((Element) imvList1.get(0)).setAttribute("id", "imv" + f.getId());

			// Append clonedFriendPaneElement to parent panel
			friendPaneElement.getParentNode().appendChild(clonedFriendPaneElement);
		}
		friendPaneElement.getParentNode().removeChild(friendPaneElement);

		// Write down the modified XML string to file
		try {
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			transformer.transform(source, result);
			String fullOutputXml = sw.toString();
			String outputXml = fullOutputXml.substring(fullOutputXml.indexOf(ANCHOR_PANE));
			String outputFxml = header + outputXml;
			FileUtils.writeStringToFile(new File(FRIEND_LIST_FILE_PATH), outputFxml, "UTF-8");
		} catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
			ExceptionUtils.log((Exception) e);
		} catch (TransformerException e) {
			ExceptionUtils.log(e);
		} catch (IOException e) {
			ExceptionUtils.log(e);
		}
	}
	
	/**
	 * To update FriendListFormUI (add text, load image,...) 
	 * 
	 * @param friendListRootPane
	 * @param friends
	 */
	private void updateControls(AnchorPane friendListRootPane, Users friends){
		
		for (int i = 0; i < friends.getData().size(); i++) {

			User friend = friends.getData().get(i);
			
			// Update textArea
			TextArea textArea = (TextArea) friendListRootPane.lookup("#txa" + friend.getId());
			textArea.setText(friend.getName());
			
			// Update imageView
			ImageView imageView = (ImageView) friendListRootPane.lookup("#imv" + friend.getId());
			Image friendPicture = friend.getPicture(); 
			if (friendPicture != null) {
				imageView.setImage(friendPicture);
			}
		}
	}
	
	/**
	 * This class is used for downloading friends photos and showing them on UI.
	 * 
	 * @author hoannguyen
	 *
	 */
	private class FriendListPictureLoader extends Task<Integer>{

		/**
		 * The friendListForm root pane, this pane is needed for updating UI
		 */
		private AnchorPane friendListRootPane;
		
		/**
		 * @param friendListRootPane the friendListRootPane to set
		 */
		public void setFriendListRootPane(AnchorPane friendListRootPane) {
			this.friendListRootPane = friendListRootPane;
		}

		@Override
		protected Integer call(){
			try{
				// Get profile pictures of friends
				Users friends = RequestedFriendsModel.getInstance().getFriends();
				for (int i = 0; i <  friends.getData().size(); i++) {
					User friend = friends.getData().get(i);
					String friendProfilePictureURL = "http://graph.facebook.com/" + friend.getId() + "/picture?width=100&height=100";
					InputStream profilePictureStream = HttpConnectionUtils.doGetInputStreamRequest(friendProfilePictureURL);
					friend.setPicture(new Image(profilePictureStream));
					
					// Update friend profile photo
					updateFriendPicture(friend);
					
					// Update the loading progress
					final float count = friends.getData().size();
					final float percent = (i + 1) / count;
					System.out.println(percent);
					Platform.runLater(new Runnable() {
						
						@Override
						public void run() {
							
							// Update progressBar control status
							ProgressBar prbPercent = (ProgressBar) friendListRootPane.lookup("#hboxPhotosLoading").lookup("#prbLoadingPhotosPercent");
							prbPercent.setProgress(percent);
							
							// Update percent label text
							Label lbPercent = (Label) friendListRootPane.lookup("#hboxPhotosLoading").lookup("#lbLoadingPhotosPercent");
							lbPercent.setText(String.valueOf(Math.round(percent * 100)) + "%");
						}
					});
				}
			}catch(Exception e){
				ExceptionUtils.log(e);
			}
			
			return null;
		}
		
		/**
		 * To update a friend picture image view on the FriendListForm with the
		 * given friend parameter.
		 * 
		 * @param f
		 */
		private void updateFriendPicture(User f){
			
			javafx.scene.Node node = friendListRootPane.lookup("#imv" + f.getId());
			if(node != null){
				
				ImageView imvFriendPicture = (ImageView) node ;
				imvFriendPicture.setImage(f.getPicture());
			}
		}
	}
	
}
